package org.expressme.ant;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Method;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
 * Generate mx:RemoteObject definition from Java interface.
 * 
 * @author Michael Liao (askxuefeng@gmail.com)
 */
public class JavaInterface2RemoteObjectTask extends Task {

    private static final String CONCURRENCY_MULTIPLE = "multiple";
    private static final String CONCURRENCY_SINGLE = "single";
    private static final String CONCURRENCY_LAST = "last";

    private boolean orderByName = false;
    private String interfaceClass;
    private String concurrency = CONCURRENCY_LAST;
    private boolean showBusyCursor = false;
    private boolean makeObjectsBindable = true;
    private String destination;
    private File outputDir;
    private String encoding = "UTF-8";
    private String handlerSuffix = "Handler";
    private String faultSuffix = "Fault";

    public JavaInterface2RemoteObjectTask() {
        init();
    }

    @Override
    public void execute() throws BuildException {
        if (interfaceClass==null)
            throw new BuildException("Attribute 'interfaceClass' must be specified.");
        if (! isExist(interfaceClass))
            throw new BuildException("Class '" + interfaceClass + "' is not exist.");
        if (! isInterface(interfaceClass))
            throw new BuildException("Class '" + interfaceClass + "' is not a Java interface.");
        if (destination==null)
            throw new BuildException("Attribute 'destination' must be specified.");
        if (outputDir==null)
            throw new BuildException("Attribute 'outputDir' must be specified.");
        if (!outputDir.isDirectory())
            throw new BuildException("Directory '" + outputDir + "' not exist.");
        if (!(CONCURRENCY_LAST.equals(concurrency)
                || CONCURRENCY_MULTIPLE.equals(concurrency)
                || CONCURRENCY_SINGLE.equals(concurrency)
            ))
        {
            throw new BuildException("Attribte 'concurrency' must be specified as 'multiple|single|last'.");
        }
        JavaInterface2RemoteObject j2r = new JavaInterface2RemoteObject();
        Class<?> clazz = forName(interfaceClass);
        String ro = j2r.toRemoteObjectString(
                clazz,
                destination,
                concurrency,
                makeObjectsBindable,
                showBusyCursor,
                orderByName
        );
        File dir = new File(outputDir + "/" + clazz.getPackage().getName().replace('.', '/'));
        if (!dir.isDirectory())
            dir.mkdirs();
        File file = new File(outputDir + "/" + interfaceClass.replace('.', '/') + "RO.as");
        Writer writer = null;
        try {
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), encoding));
            writer.write(ro);
        }
        catch (IOException e) {
            throw new BuildException("Cannot write file: " + file.getPath());
        }
        finally {
            if (writer!=null) {
                try {
                    writer.close();
                }
                catch(IOException e) {
                }
            }
        }
    }

    Class<?> forName(String className) throws BuildException {
        try {
            return Class.forName(className);
        }
        catch(ClassNotFoundException e) {
            throw new BuildException(e);
        }
    }

    boolean isInterface(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return clazz.isInterface();
        }
        catch(ClassNotFoundException e) {
            return false;
        }
    }

    boolean isExist(String className) {
        try {
            Class.forName(className);
        }
        catch(ClassNotFoundException e) {
            return false;
        }
        return true;
    }

    public void setInterfaceClass(String interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    public void setOrderByName(boolean orderByName) {
        this.orderByName = orderByName;
    }

    public void setDestination(String destination) {
        this.destination = destination;
    }

    public void setConcurrency(String concurrency) {
        this.concurrency = concurrency;
    }

    public void setShowBusyCursor(boolean showBusyCursor) {
        this.showBusyCursor = showBusyCursor;
    }

    public void setMakeObjectsBindable(boolean makeObjectsBindable) {
        this.makeObjectsBindable = makeObjectsBindable;
    }

    public void setOutputDir(File outputDir) {
        this.outputDir = outputDir;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public String getHandler(Method method) {
        return method.getName() + handlerSuffix;
    }

    public String getFault(Method method) {
        return method.getName() + faultSuffix;
    }

    public void setHandlerSuffix(String handlerSuffix) {
        this.handlerSuffix = handlerSuffix;
    }

    public void setFaultSuffix(String faultSuffix) {
        this.faultSuffix = faultSuffix;
    }

}
